package simpleftp.server;

import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.Void;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import javax.swing.JFileChooser;
import javax.swing.tree.DefaultMutableTreeNode;

import simpleftp.client.control.Command;
import simpleftp.client.control.Uploader;

/**
 * ServeurFTP is a simple package that implements a Java FTP server. With
 * ServeurFTP, you can connect to an FTP client and download multiple files. 
 */
public class FTPServer implements Callable<Void>
{
    private static boolean DEBUG = false;
    private String response, user;
    private int validations;
    private File f;
    private StringBuilder sb;
    private int portnum1, portnum2;
    private Properties p;
    private ServerSocket dl;
    private Socket dl2;
    private FileInputStream fis;
    private Socket socket = null;
    private BufferedReader reader = null;
    private BufferedWriter writer = null;
    private ObjectOutputStream output;
    private DefaultMutableTreeNode treeRoot;

    public FTPServer(Socket s) {
        socket = s;
        validations = 0;
        try {
            p = new Properties();
            fis = new FileInputStream("users.properties");
            p.load(fis);
            try {reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                // initialize tree to send and stream sender
                output = new ObjectOutputStream(socket.getOutputStream());
                f = new File(System.getProperty("user.dir"));
                treeRoot = addNodes(treeRoot, f);


                sendLine("220 ");
                validations++;

                System.out.println("FTP Server : new client from " + socket.getInetAddress().getHostAddress());
            } catch (IOException e) {
                try {
                    sendLine("error");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            }
        } catch (IOException ioex) {
            System.out.println("Error: Properties File didn't load");
            try {
                sendLine("error ");
            } catch (IOException e) {
                e.printStackTrace();
            }
            ioex.printStackTrace();
        }
    }


    public Void call() {
        try {
        	//check if the user is correct
            response = readLine();
            if (response.startsWith("USER")) {
                if (p.containsKey(response.substring(5))) {
                    user = response.substring(5);
                    System.out.println("Server knows user " + response);
                    sendLine("331 ");
                    validations++;
                } else {
                    System.out.println("Server does no know user " + response);
                    sendLine("error ");
                }
            }
          //check if the password is correct
            response = readLine();
            if (response.startsWith("PASS")) {
                if (p.get((String) user).equals(response.substring(5))) {
                    System.out.println("Server validates pass " + response);
                    sendLine("230 ");
                    validations++;
                } else {
                    sendLine("error ");
                }
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            try {
                sendLine("error ");
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        
        //begin communication
        f = new File(System.getProperty("user.dir"));
        while (validations == 3) {
            try {
                response = readLine();

                switch (Command.valueOf(response.split(" ")[0])) {

                    case PWD:
                        sendLine("257 " + System.getProperty("user.dir"));
                        break;

                    case LIST:
                    	//get the list of file present in the directory
                        sb = new StringBuilder("200 ");
                        String[] fileNames = f.list();
                        for (String fileName : fileNames) {
                            sb = sb.append(fileName + "|");
                        }
                        //send this tree to the client
                        sendLine(sb.toString());
                        sendSerializableObject();
                        break;

                    case CWD:
                        System.out.println("CWD");
                        System.setProperty("user.dir", response.substring(4));
                        System.out.println("CWD new current directory:"
                                + System.getProperty("user.dir").toString());
                        f = new File(System.getProperty("user.dir"));
                        sendLine("250 ");
                        break;

                    case PASV:
                        pasv();
                        break;

                    case STOR:
                        stor();
                        break;
                        
                    case HELLO:
                    	download();
                    	break;

                    case QUIT:
                        socket.close();
                        System.out.println("QUIT :Goodbye");
                        validations = 0;
                        break;

                    default:
                        System.out.println("Unknown Command.");
                        sendLine("error");
                        break;

                }
            } catch (IOException e) {
                try {
                    sendLine("error");
                } catch (IOException e1) {

                    e1.printStackTrace();
                }
                e.printStackTrace();
            }

        }
        Void Void = null;
        return Void;
    }

    //SEND SERIALIZABLE OBJECT OVER THE NETWORK

    private void sendSerializableObject() {
        try {
            treeRoot = null;
            output.writeObject(addNodes(treeRoot, f));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Add nodes from under "dir" into curTop. Highly recursive. FROM THE WEB
     * http://www.java2s.com/Code/Java/File-Input-Output/DisplayafilesysteminaJTreeview.htm
     * */
    private DefaultMutableTreeNode addNodes(DefaultMutableTreeNode curTop, File dir) {
        String curPath = dir.getPath();
        DefaultMutableTreeNode curDir = new DefaultMutableTreeNode(curPath);
        if (curTop != null) { // should only be null at root
            curTop.add(curDir);
        }
        Vector<String> ol = new Vector<String>();
        String[] tmp = dir.list();
        for (int i = 0; i < tmp.length; i++)
            ol.addElement(tmp[i]);
        Collections.sort(ol, String.CASE_INSENSITIVE_ORDER);
        File f;
        Vector<String> files = new Vector<String>();
        // Make two passes, one for Dirs and one for Files. This is #1.
        for (int i = 0; i < ol.size(); i++) {
            String thisObject = (String) ol.elementAt(i);
            String newPath;
            if (curPath.equals("."))
                newPath = thisObject;
            else
                newPath = curPath + File.separator + thisObject;
            if ((f = new File(newPath)).isDirectory())
                addNodes(curDir, f);
            else
                files.addElement(thisObject);
        }
        // Pass two: for files.
        for (int fnum = 0; fnum < files.size(); fnum++)
            curDir.add(new DefaultMutableTreeNode(files.elementAt(fnum)));
        return curDir;
    } 
    
    
    
    //send raw information on the network
    private void sendLine(String line) throws IOException {
        if (socket == null) {
            throw new IOException("FTPServer is not connected.");
        }
        try {
            writer.write(line + "\r\n");
            writer.flush();
            if (DEBUG) {
                System.out.println("> " + line);
            }
        } catch (IOException e) {
            socket = null;
            throw e;
        }
    }
    
  //read raw information on the network
    private String readLine() throws IOException {
        String line = reader.readLine();
        if (DEBUG) {
            System.out.println("< " + line);
        }
        return line;
    }
    
  //Receive a new file and save it
    private void stor() {
        try {
        	//accept new socket connection
            dl2 = dl.accept();
            sendLine("150 ");
            
            //create file on the placement of the output
            OutputStream os = new FileOutputStream(new File(
                    System.getProperty("user.dir")
                            + System.getProperty("file.separator")
                            + response.substring(5)));

            System.out.println("===>"+System.getProperty("user.dir")+ System.getProperty("file.separator")+response.substring(5)+"///"+response);
            
            //read the flux and put it in the file
            InputStream is = dl2.getInputStream();
            byte[] buffer = new byte[4096];
            int bytesRead = 0;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
            os.close();
            is.close();
            
            //send protocol of end of transfert
            sendLine("226 ");
            dl.close();
            dl2.close();
            System.out.println("STOR " + response.split(" ")[1]
                    + " : File received");
        } catch (IOException e) {
            try {
                sendLine("error");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    //Set the connection to passive mode
    private void pasv() {
        try {
            dl = new ServerSocket(0);
            portnum1 = dl.getLocalPort() / 256;
            portnum2 = dl.getLocalPort() % 256;
            sendLine("227 ("
                    + InetAddress.getLocalHost().getHostAddress()
                    .replace('.', ',') + "," + portnum1 + ","
                    + portnum2 + ")");
            System.out.println("PASV success");
        } catch (IOException e) {
            try {
                sendLine("error ");
                System.out.println("pasv error");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    public static void setDebug(boolean b) {
        DEBUG = b;
    }






//download file
public void download() throws IOException
{
	try {
		//begin connection
        dl2 = dl.accept();
        sendLine("150 ");
        
        
        //read the file and send it to the flux
        File file=null;// need to get the file ask
        
        
        
        BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream os =  new BufferedOutputStream(dl2.getOutputStream());
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        
        //send file
        while ((bytesRead = is.read(buffer)) != -1) {
        	os.write(buffer, 0, bytesRead);
        }
        
        os.flush();
        os.close();
        is.close();
        
        //end of transfert
        sendLine("226 ");
        dl.close();
        dl2.close();
        System.out.println("RETR" + response.split(" ")[1]
                + " : File send");
    } catch (IOException e) {
        try {
            sendLine("error");
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        e.printStackTrace();
    }
}

}